#include <stdio.h>
#include <stdlib.h>
#include <glob.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <dirent.h>
#include "media_lib.h"

// 声明静态辅助函数
static int __open_next(glob_t chn_file, int cur_index);
static struct chn_context_st *__parse_chn(const char *path);
static int parse_media_lib(const char *path);
static off_t skip_mp3_id3_tag(int fd);

// 频道上下文结构体（存储单个频道的所有信息）
struct chn_context_st {
    struct mlib_list_entry list;  // 频道ID和描述
    glob_t mp3_path;              // 该频道下所有MP3文件路径
    int fd;                       // 当前打开的MP3文件描述符
    int cur_file_idx;             // 当前播放的MP3文件索引
};

// 全局变量：存储所有频道的指针（下标为频道ID）
static struct chn_context_st *all_chns[MAXCHN_NR] = {};
static int chn_nr = 0;  // 当前有效频道数量
static chnid_t cur_id = MIN_CHN_ID;  // 全局频道ID计数器（替换__parse_chn中的静态变量）

// 解析媒体库根目录，获取所有频道
static int parse_media_lib(const char *path) {
    DIR *dp = NULL;
    struct dirent *entry = NULL;
    char buf[BUFSIZE] = {};
    struct chn_context_st *ret = NULL;

    // 打开媒体库目录
    dp = opendir(path);
    if (NULL == dp) {
        perror("opendir(media lib) failed");
        return -1;
    }

    // 遍历目录下的所有子目录（每个子目录是一个频道）
    while (1) {
        entry = readdir(dp);
        if (NULL == entry) break;  // 遍历结束

		// 跳过 . 和 .. 目录
		if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
			continue;

		memset(buf, '\0', BUFSIZE);
		// 格式："path/entry->d_name"，预留1字节给'\0'，避免溢出
		snprintf(buf, BUFSIZE - 1, "%s/%s", path, entry->d_name);
		// 检查是否截断（若返回值 >= BUFSIZE，说明路径过长）
		if (snprintf(buf, BUFSIZE - 1, "%s/%s", path, entry->d_name) >= BUFSIZE) {
			fprintf(stderr, "[ERROR] 路径过长：%s/%s（超过%d字节）\n", path, entry->d_name, BUFSIZE);
			continue;
		}

        // 解析该频道的信息（MP3文件、描述）
        ret = __parse_chn(buf);
        if (NULL == ret) {
            fprintf(stderr, "[WARN] 频道 %s 解析失败\n", buf);
            continue;
        }

        // 将频道存入全局数组
        all_chns[ret->list.chnid] = ret;
        chn_nr++;
    }

    closedir(dp);
    return 0;
}

// 解析单个频道（读取描述文件和MP3文件）
static struct chn_context_st *__parse_chn(const char *path) {
	struct chn_context_st *me = NULL;
	FILE *fp = NULL;
	char buf[BUFSIZE] = {};
	size_t n = 0;
	int i = -1;
	int opened_idx = -1;

    // 1. 分配频道上下文内存
    me = malloc(sizeof(struct chn_context_st));
    if (NULL == me) {
        perror("malloc chn_context_st failed");
        return NULL;
    }
    // 初始化成员（避免野指针/无效值）
    me->fd = -1;
    me->cur_file_idx = -1;
    me->list.descr = NULL;
    memset(&me->mp3_path, 0, sizeof(glob_t));

    // 2. 检查频道ID是否超过最大值（避免数组越界）
    if (cur_id >= MAXCHN_NR) {
        fprintf(stderr, "[ERROR] 频道数量超过最大值 %d\n", MAXCHN_NR);
        free(me);
        return NULL;
    }
    me->list.chnid = cur_id;

    // 3. 读取频道描述文件（descr.txt）
    memset(buf, '\0', BUFSIZE);
    strcpy(buf, path);
    strcat(buf, "/");
    strcat(buf, CHN_DECR_NAME);
    fp = fopen(buf, "r");
    if (NULL == fp) {
        fprintf(stderr, "[ERROR] 找不到描述文件：%s\n", buf);
        free(me);
        return NULL;
    }
    // 读取描述内容（getline 会自动分配内存）
    if (-1 == getline(&(me->list.descr), &n, fp)) {
        fprintf(stderr, "[ERROR] 描述文件 %s 为空\n", buf);
        fclose(fp);
        free(me);
        return NULL;
    }
    fclose(fp);  // 关闭描述文件

    // 4. 匹配该频道下的所有MP3文件
    memset(buf, '\0', BUFSIZE);
    strcpy(buf, path);
    strcat(buf, "/*.mp3");
    int glob_ret = glob(buf, 0, NULL, &me->mp3_path);
    if (glob_ret == GLOB_NOMATCH) {
        fprintf(stderr, "[ERROR] 频道 %s 下没有MP3文件\n", path);
        free(me->list.descr);
        free(me);
        return NULL;
    } else if (glob_ret != 0) {
        fprintf(stderr, "[ERROR] glob匹配MP3失败，错误码：%d\n", glob_ret);
        free(me->list.descr);
        free(me);
        return NULL;
    }
    // 打印匹配到的MP3文件（确认路径正确）
    fprintf(stdout, "[DEBUG] 频道 %s 匹配到 %zu 个MP3文件：\n", path, me->mp3_path.gl_pathc);
    for (size_t j = 0; j < me->mp3_path.gl_pathc; j++) {
        fprintf(stdout, "  - %s\n", me->mp3_path.gl_pathv[j]);
    }

    // 5. 尝试打开第一个MP3文件（直到成功或全部失败）
    fprintf(stdout, "[DEBUG] 开始尝试打开MP3文件...\n");
    while (i < (int)me->mp3_path.gl_pathc) {
        int fd;
        if (i == -1) {
            opened_idx = 0;  // 首次尝试：打开第0个文件
        } else {
            opened_idx = i;  // 后续尝试：打开第i个文件
            if (me->fd != -1) {
                close(me->fd);  // 关闭上一个失败的文件描述符
                me->fd = -1;
            }
        }

        // 关键：调用 __open_next 打开文件（必须打印详细日志）
        fprintf(stdout, "[DEBUG] 准备调用 __open_next，当前索引：%d\n", opened_idx);
        fd = __open_next(me->mp3_path, opened_idx);

        if (fd < 0) {
            fprintf(stderr, "[WARN] 第 %d 个MP3文件打开失败，尝试下一个\n", opened_idx);
            i++;
            continue;
        }

        // 打开成功：记录文件描述符和索引
        me->fd = fd;
        me->cur_file_idx = opened_idx;
        fprintf(stdout, "[DEBUG] 成功打开MP3文件：%s，描述符：%d\n", 
                me->mp3_path.gl_pathv[opened_idx], fd);
        break;
    }

    // 6. 检查是否所有MP3都打开失败
    if (me->fd < 0) {
        fprintf(stderr, "[ERROR] 频道 %s 所有MP3文件打开失败\n", path);
        globfree(&me->mp3_path);  // 释放glob资源
        free(me->list.descr);     // 释放描述字符串
        free(me);                 // 释放频道上下文
        return NULL;
    }

    // 频道ID递增（下一个频道用）
    cur_id++;
    return me;
}

// 打开下一个MP3文件（打印详细错误信息）
static int __open_next(glob_t chn_file, int cur_index) {
    int fd = -1;
    int next_index = 0;

    // 1. 计算下一个文件的索引（循环播放）
    if (cur_index < 0) {
        next_index = 0;
    } else {
        next_index = (cur_index + 1) % (int)chn_file.gl_pathc;
    }

    // 2. 检查索引是否有效（避免越界）
    if (next_index < 0 || next_index >= (int)chn_file.gl_pathc) {
        fprintf(stderr, "[ERROR] __open_next：无效索引 %d（总文件数 %zu）\n", 
                next_index, chn_file.gl_pathc);
        return -1;
    }

    // 3. 获取文件路径并打印（确认路径正确）
    const char *file_path = chn_file.gl_pathv[next_index];
    if (NULL == file_path || *file_path == '\0') {
        fprintf(stderr, "[ERROR] __open_next：文件路径为空（索引 %d）\n", next_index);
        return -1;
    }
    fprintf(stdout, "[DEBUG] __open_next：尝试打开文件：%s\n", file_path);

    // 4. 执行open操作并打印详细错误
    fd = open(file_path, O_RDONLY);
    if (fd < 0) {
        // 强制打印错误码和原因（errno 是系统全局变量，记录最近错误）
        fprintf(stderr, "[ERROR] __open_next：打开失败！路径：%s，错误码：%d，原因：%s\n", 
                file_path, errno, strerror(errno));
    } else {
           skip_mp3_id3_tag(fd); // 跳过ID3标签
    	   fprintf(stdout, "[DEBUG] __open_next：打开成功！描述符：%d（已跳过ID3标签）\n", fd);
    }

    return fd;
}

// 跳过MP3文件的ID3v1/v2标签（返回跳过标签后的文件偏移量）
static off_t skip_mp3_id3_tag(int fd) {
    unsigned char buf[10];
    off_t offset = 0;

    // 读取文件开头10字节，判断是否有ID3v2标签
    if (read(fd, buf, 10) != 10) {
        lseek(fd, 0, SEEK_SET); // 读失败，回到开头
        return 0;
    }

    // ID3v2标签标识：前3字节是"ID3"，第4-5字节是版本，第6字节是标志，第7-10字节是标签大小
    if (buf[0] == 'I' && buf[1] == 'D' && buf[2] == '3') {
        // 计算ID3v2标签大小（7-10字节是大端格式，共4字节，不包含最高位）
        off_t tag_size = ((buf[6] & 0x7F) << 21) | 
                        ((buf[7] & 0x7F) << 14) | 
                        ((buf[8] & 0x7F) << 7) | 
                        (buf[9] & 0x7F);
        offset = 10 + tag_size; // 标签总长度 = 10字节头 + 标签大小
        lseek(fd, offset, SEEK_SET);
        printf("[DEBUG] 跳过ID3v2标签，偏移量：%ld字节\n", offset);
        return offset;
    }

    // 无ID3v2标签，回到开头
    lseek(fd, 0, SEEK_SET);
    return 0;
}

// 对外接口：获取频道列表（从已初始化的 all_chns 数组提取，复用现有逻辑）
int mlib_get_chn_list(struct mlib_list_entry **chn_list, int *chn_count) {
    // 1. 若未解析媒体库，先解析（根目录：/home/xiaowei/media）
    if (chn_nr == 0) {
        const char *media_root = "/home/xiaowei/media"; // 媒体库根目录
        printf("[DEBUG] 首次调用，解析媒体库根目录：%s\n", media_root);
        if (parse_media_lib(media_root) < 0) {
            fprintf(stderr, "[ERROR] 解析媒体库失败，无有效频道\n");
            return -1;
        }
        if (chn_nr == 0) {
            fprintf(stderr, "[ERROR] 媒体库 %s 下无有效频道\n", media_root);
            return -1;
        }
    }

    // 2. 从 all_chns 数组中收集有效频道（all_chns[chnid] != NULL）
    *chn_list = (struct mlib_list_entry *)malloc(sizeof(struct mlib_list_entry) * chn_nr);
    if (*chn_list == NULL) {
        perror("malloc mlib_list_entry failed");
        return -1;
    }

    int idx = 0;
    for (chnid_t i = MIN_CHN_ID; i < MAXCHN_NR; i++) {
        if (all_chns[i] != NULL) {
            // 复制频道ID和描述（避免直接引用全局变量）
            (*chn_list)[idx].chnid = all_chns[i]->list.chnid;
            (*chn_list)[idx].descr = strdup(all_chns[i]->list.descr);
            if ((*chn_list)[idx].descr == NULL) {
                perror("strdup descr failed");
                // 回滚：释放已分配的描述
                for (int j = 0; j < idx; j++) free((*chn_list)[j].descr);
                free(*chn_list);
                *chn_list = NULL;
                return -1;
            }
            idx++;
        }
    }

    // 3. 设置返回值（实际有效频道数）
    *chn_count = idx;
    printf("[DEBUG] 从媒体库提取 %d 个有效频道\n", *chn_count);
    return 0;
}

int mlib_read_chn_data(chnid_t chnid, void *buf, size_t size) {
	// 参数合法性检查
	if (chnid < MIN_CHN_ID || chnid >= MAXCHN_NR || all_chns[chnid] == NULL) {
		errno = EINVAL;
		perror("mlib_read_chn_data：无效频道ID");
		return -1;
	}
	if (buf == NULL || size == 0) {
		errno = EINVAL;
		perror("mlib_read_chn_data：无效缓冲区");
		return -1;
	}

	struct chn_context_st *chn = all_chns[chnid];
	ssize_t total_read = 0;
	ssize_t read_ret;

    // 确保读取大小是帧的整数倍（避免分割帧）
    #define MP3_FRAME_SIZE 1152  // 补充定义帧大小（如果头文件未定义）
    size = (size / MP3_FRAME_SIZE) * MP3_FRAME_SIZE;
    if (size == 0) size = MP3_FRAME_SIZE;  // 至少读1帧

    while (total_read < size) {
        read_ret = read(chn->fd, (char *)buf + total_read, size - total_read);
        if (read_ret < 0) {
            if (errno == EINTR) continue;
            perror("[DEBUG] mlib_read_chn_data read failed");
            return -1;
        } else if (read_ret == 0) {
            // 文件读完，循环播放：打开下一个文件
            int new_fd;
            // 关键修正：传递正确的参数（glob_t文件列表 + 当前索引）
            new_fd = __open_next(chn->mp3_path, chn->cur_file_idx);
            if (new_fd < 0) {
                fprintf(stderr, "[DEBUG] 频道%d 无文件可播放\n", chnid);
                return total_read > 0 ? total_read : -1;
            }
            // 更新当前文件描述符和索引（循环逻辑）
            close(chn->fd);  // 关闭旧文件
            chn->fd = new_fd;
            chn->cur_file_idx = (chn->cur_file_idx + 1) % (int)chn->mp3_path.gl_pathc;
            continue;
        }

        total_read += read_ret;
    }

    // ...（后面帧头打印逻辑不变）

    return total_read;
}

// 对外接口：销毁媒体库资源
void mlib_destroy(void) {
    for (int i = MIN_CHN_ID; i < MAXCHN_NR; i++) {
        if (all_chns[i] != NULL) {
            // 关闭文件描述符
            if (all_chns[i]->fd >= 0) {
                close(all_chns[i]->fd);
                all_chns[i]->fd = -1;
            }
            // 释放glob资源
            globfree(&all_chns[i]->mp3_path);
            // 释放描述字符串
            if (all_chns[i]->list.descr != NULL) {
                free(all_chns[i]->list.descr);
                all_chns[i]->list.descr = NULL;
            }
            // 释放频道上下文
            free(all_chns[i]);
            all_chns[i] = NULL;
        }
    }
    chn_nr = 0;          // 重置频道数量
    cur_id = MIN_CHN_ID; // 关键：重置频道ID计数器，下次解析从1开始
}